package com.zmn.oms.zmn.normal.business.impl.review;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.reflect.BeanMapper;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.manager.redis.RedisManager;
import com.zmn.mcc.common.dto.staff.StaffDRO;
import com.zmn.mcc.dubbo.interfaces.staff.StaffListRemoteService;
import com.zmn.oms.business.interfaces.log.OrderLogBService;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderLogConsts;
import com.zmn.oms.common.constant.OrderStatusConsts;
import com.zmn.oms.common.constant.RedisKeyConsts;
import com.zmn.oms.common.dto.DingTalkReviewResultDTO;
import com.zmn.oms.common.dto.OmsBaseOperator;
import com.zmn.oms.common.dto.OmsOperator;
import com.zmn.oms.common.dto.RedisReviewDTO;
import com.zmn.oms.common.exception.OmsBaseException;
import com.zmn.oms.common.vo.KVDict;
import com.zmn.oms.model.bo.work.review.RefundReviewBO;
import com.zmn.oms.model.dto.order.pay.DuplicatePayRefundDTO;
import com.zmn.oms.model.dto.work.compensate.CompensateDTO;
import com.zmn.oms.model.dto.work.refund.RefundDTO;
import com.zmn.oms.model.entity.log.OrderLog;
import com.zmn.oms.model.entity.review.OmsOrderReview;
import com.zmn.oms.model.entity.review.OrderReviewDbQuery;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.services.interfaces.review.OmsOrderReviewService;
import com.zmn.oms.zmn.business.interfaces.pay.OrderPayBService;
import com.zmn.oms.zmn.normal.business.interfaces.refund.RefundBService;
import com.zmn.oms.zmn.normal.business.interfaces.review.OmsOrderReviewBService;
import com.zmn.tapi.common.dingtalk.FormComponentValueDIO;
import com.zmn.tapi.common.dingtalk.ProcessInstanceDIO;
import com.zmn.tapi.common.dingtalk.TerminateDIO;
import com.zmn.tapi.dubbo.interfaces.dingtalk.DingTalkAuditService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName OmsOrderReviewBServiceImpl
 * @description:
 * @author: wangyanci
 * @create: 2021-08-09 15:05
 * @Version 1.0
 **/
@Slf4j
@Service
public class OmsOrderReviewBServiceImpl implements OmsOrderReviewBService {

    // 拒绝
    private static final String REFUSE = "refuse";

    // 通过
    private static final String AGREE = "agree";

    @NacosValue(value = "${refund.dTalk.process.code:PROC-8101F588-31A1-4448-96D5-9F9DF8415FAE}", autoRefreshed = true)
    private String refundDTalkProcessCode;

    // 钉钉审批类型集合
    private static final List<Integer> DINGDTALKAUDIT_TYPE_LIST = Lists.newArrayList(
            OrderConsts.OMS_ORDER_REVIEW_REFUND
    );

    @Autowired
    private OmsOrderReviewService orderReviewService;
    @Autowired
    private RefundBService refundBService;
    @Autowired
    private OrderPayBService orderPayBService;
    @Autowired
    private OrderLogBService orderLogBService;

    @Reference(version = com.zmn.tapi.dubbo.DubboConsts.INTERFACE_VERSION, check = false)
    private DingTalkAuditService dingTalkAuditService;
    @Reference(version = com.zmn.mcc.dubbo.dto.DubboConsts.INTERFACE_VERSION, check = false)
    private StaffListRemoteService staffListRemoteService;

    @Resource
    private RedisManager redisManager;

    @Override
    public void initiateCompensateReview(OrderWork compensateOrderWork, CompensateDTO compensateDTO) throws OmsBaseException {
        OmsOrderReview orderReview = orderReviewService.getOmsOrderReviewByOrderIdAndWorkIdAndType(compensateOrderWork.getOrderId(), compensateOrderWork.getWorkId(), OrderConsts.OMS_ORDER_REVIEW_COMPENSATE);
        if (Objects.nonNull(orderReview) && Objects.equals(orderReview.getReviewStatus(), OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK)) {
            this.stopReview(orderReview, compensateDTO);
        }
        OmsOrderReview omsOrderReview = this.generateOrderReviewModel(OrderConsts.OMS_ORDER_REVIEW_COMPENSATE, compensateOrderWork.getOrderId(), compensateOrderWork.getWorkId(), JSON.toJSONString(compensateDTO), compensateDTO);
        orderReviewService.insertOmsOrderReview(omsOrderReview);
    }

    @Override
    public void initiateRefundReview(OrderWork refundOrderWork, RefundDTO refundDTO) throws OmsBaseException {

        OmsOrderReview orderReview = orderReviewService.getOmsOrderReviewByOrderIdAndWorkIdAndType(refundOrderWork.getOrderId(), refundOrderWork.getWorkId(), OrderConsts.OMS_ORDER_REVIEW_REFUND);
        if (Objects.nonNull(orderReview) && Objects.equals(orderReview.getReviewStatus(), OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK)) {
            this.stopReview(orderReview, refundDTO);
        }

        List<String> reasonNameList = refundDTO.getReasonList()
                .stream()
                .map(KVDict::getText)
                .collect(Collectors.toList());
        String reasonName = String.format("%s；%s", StringUtils.join(Optional.ofNullable(reasonNameList).orElse(Lists.newArrayList()), "；"), refundDTO.getContent());

        ArrayList<FormComponentValueDIO> formComponentValueDIOS = Lists.newArrayList(
                FormComponentValueDIO.builder()
                        .name("工单号")
                        .value(String.valueOf(refundOrderWork.getWorkId()))
                        .build(),
                FormComponentValueDIO.builder()
                        .name("退款金额（元）")
                        .value(NumberUtil.convertMoneyToYuan2(refundDTO.getRefundAmount()))
                        .build(),
                FormComponentValueDIO.builder()
                        .name("退款原因")
                        .value(reasonName)
                        .build()
        );
        ProcessInstanceDIO processInstanceDIO = ProcessInstanceDIO.builder()
                .originatorUserId(refundDTO.getDTalkId())
                .componentValues(formComponentValueDIOS)
                .deptId(refundDTO.getDingTalkDeptId())
                .processCode(refundDTalkProcessCode)
                .build();
        log.info("dingTalkAuditService.createProcessInstance params={}", JSON.toJSONString(processInstanceDIO));
        ResponseDTO<String> processInstance = dingTalkAuditService.createProcessInstance(processInstanceDIO);
        log.info("dingTalkAuditService.createProcessInstance result={}", JSON.toJSONString(processInstance));

        // 记录退款发起钉钉审核日志
        OrderLog orderLog = orderLogBService.generateOrderLog(refundOrderWork.getOrderId(), refundOrderWork.getWorkId(),
                OrderLogConsts.ORDER_LOG_TYPE_REFUND_AUDIT,
                "系统", 1L, GlobalConsts.OPERATE_USER_TYPE_SYSTEM, null, new Date(refundDTO.getOperateTime().getTime() + 1));

        if (!processInstance.isSuccess() || StringUtil.isBlank(processInstance.getData())) {
            //orderLog.setContent("发起钉钉审核失败：消息发送失败");
            //orderLogBService.save(orderLog);
            throw new OmsBaseException(processInstance.getMessage());
        }
        orderLog.setContent("待客诉主管审核(钉钉)");
        orderLogBService.save(orderLog);

        // 生成审批记录
        OmsOrderReview omsOrderReview = this.generateOrderReviewModel(OrderConsts.OMS_ORDER_REVIEW_REFUND, refundOrderWork.getOrderId(), refundOrderWork.getWorkId(), JSON.toJSONString(refundDTO), refundDTO);
        omsOrderReview.setProcessInstanceId(processInstance.getData());
        orderReviewService.insertOmsOrderReview(omsOrderReview);

        String key = String.format(RedisKeyConsts.ORDER_REVIEW_CODE_KEY, processInstance.getData());
        RedisReviewDTO refundReviewDTO = new RedisReviewDTO();
        refundReviewDTO.setOrderId(refundOrderWork.getOrderId());
        refundReviewDTO.setWorkId(refundOrderWork.getWorkId());
        refundReviewDTO.setReviewType(OrderConsts.OMS_ORDER_REVIEW_REFUND);
        // 30天
        redisManager.setex(key, JSON.toJSONString(refundReviewDTO), 86400 * 30);
    }

    @Override
    public void stopReview(OmsOrderReview orderReview, OmsBaseOperator omsBaseOperator) throws OmsBaseException {

        if (Objects.isNull(orderReview)) {
            return;
        }

        if (!Objects.equals(orderReview.getReviewStatus(), OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK)) {
            throw new OmsBaseException("审批状态非待审核，不允许撤回！");
        }

        if (DINGDTALKAUDIT_TYPE_LIST.contains(orderReview.getReviewType())) {
            try {
                TerminateDIO terminateDIO = TerminateDIO
                        .builder()
                        .processInstanceId(orderReview.getProcessInstanceId())
                        .isSystem(true)
                        .build();
                log.info("dingTalkAuditService.terminateProcessInstance params={}", terminateDIO);
                ResponseDTO<Boolean> responseDTO = dingTalkAuditService.terminateProcessInstance(terminateDIO);
                log.info("dingTalkAuditService.terminateProcessInstance result={}", responseDTO);
//        if (!responseDTO.isSuccess() || !responseDTO.getData()) {
//            throw new OmsBaseException(responseDTO.getMessage());
//        }
            } catch (Exception ex) {
                log.error("dingTalkAuditService error：", ex);
            }
        }


        OmsOrderReview deleteOrderReview = new OmsOrderReview();
        deleteOrderReview.setReviewId(orderReview.getReviewId());
        deleteOrderReview.setOrderId(orderReview.getOrderId());
        orderReviewService.deleteById(deleteOrderReview);

        if (StringUtil.isNotBlank(orderReview.getProcessInstanceId())) {
            String key = String.format(RedisKeyConsts.ORDER_REVIEW_CODE_KEY, orderReview.getProcessInstanceId());
            redisManager.del(key);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void reviewCallBack(DingTalkReviewResultDTO reviewResultDTO, RedisReviewDTO redisReviewDTO) throws OmsBaseException {
        log.info("com.zmn.oms.services.impl.review.OmsOrderReviewBServiceImpl.reviewCallBack reviewResultDTO={},redisReviewDTO={}", reviewResultDTO, redisReviewDTO);
        OrderReviewDbQuery query = new OrderReviewDbQuery();
        query.setOrderId(redisReviewDTO.getOrderId());
        query.setWorkId(redisReviewDTO.getWorkId());
        query.setProcessInstanceId(reviewResultDTO.getProcessInstanceId());
        OmsOrderReview orderReview = orderReviewService.getOmsOrderReviewByQuery(query);
        if (Objects.isNull(orderReview)) {
            log.error("未找到审批记录！");
            return;
        }
        if (!Objects.equals(orderReview.getReviewStatus(), OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK)) {
            log.error("审批状态不在待审批！");
            return;
        }

        OmsOrderReview updateOrderReview = new OmsOrderReview();
        updateOrderReview.setReviewId(orderReview.getReviewId());
        updateOrderReview.setOrderId(orderReview.getOrderId());
        updateOrderReview.setWorkId(orderReview.getWorkId());
        updateOrderReview.setReviewType(orderReview.getReviewType());
        updateOrderReview.setReviewStatus(AGREE.equals(reviewResultDTO.getResult()) ? OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK_SUCCESS : OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK_FAIL);
        Date now = DateUtil.getNow();
        updateOrderReview.setReviewTime(now);
        updateOrderReview.setUpdateTime(now);
        // 审核人
        String reviewDingTalkId = reviewResultDTO.getStaffId();
        log.info("staffListRemoteService.getStaffByDingTalkId params={}", reviewDingTalkId);
        ResponseDTO<StaffDRO> staff = staffListRemoteService.getStaffByDingTalkId(reviewDingTalkId);
        log.info("staffListRemoteService.getStaffByDingTalkId result={}", staff);
        if (staff.isSuccess() && Objects.nonNull(staff.getData())) {
            updateOrderReview.setReviewer(staff.getData().getRealName());
            updateOrderReview.setReviewerId(String.valueOf(staff.getData().getStaffId()));
        }
        orderReviewService.updateById(updateOrderReview);

        switch (redisReviewDTO.getReviewType()) {
            case OrderConsts.OMS_ORDER_REVIEW_REFUND:
                RefundReviewBO refundReviewBO = BeanMapper.map(updateOrderReview, RefundReviewBO.class);
                refundReviewBO.setReviewData(orderReview.getReviewData());
                refundReviewBO.setResultStatus(REFUSE.equals(reviewResultDTO.getResult()) ? OrderStatusConsts.WORK_RESULT_DISCARD : null);
                refundReviewBO.setOperator(updateOrderReview.getReviewer());
                refundBService.refundReviewComplete(refundReviewBO);
                break;
            case OrderConsts.OMS_ORDER_REVIEW_DUPLICATE_PAY_REFUND:
                DuplicatePayRefundDTO dto = JSON.parseObject(orderReview.getReviewData(), DuplicatePayRefundDTO.class);

                String operator = "系统";
                Long operatorId = Long.valueOf(GlobalConsts.OPERATE_USER_TYPE_SYSTEM);
                Integer operatorType = GlobalConsts.OPERATE_USER_TYPE_SYSTEM;
                StaffDRO staffData = staff.getData();
                if (Objects.nonNull(staffData)) {
                    operator = staffData.getRealName();
                    operatorId = Long.valueOf(staffData.getStaffId());
                    operatorType = GlobalConsts.OPERATE_USER_TYPE_STAFF;
                }

                if (Objects.equals(updateOrderReview.getReviewStatus(), OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK_FAIL)) {
                    // 不同意退款，记录重复支付审核结果日志
                    OrderLog orderLog = orderLogBService.generateOrderLog(dto.getOrderId(), dto.getWorkId(),
                            OrderLogConsts.ORDER_LOG_TYPE_DUPLICATE_PAY_REFUND_AUDIT_RESULT,
                            operator, operatorId, operatorType, String.format("%s不同意退款", operator), null);
                    orderLogBService.save(orderLog);
                    break;
                }

                // 发起退款
                dto.setOperator(operator);
                dto.setOperatorId(operatorId);
                dto.setOperatorType(operatorType);
                orderPayBService.duplicatePayRefund(dto);
                break;
        }

        String redisKey = String.format(RedisKeyConsts.ORDER_REVIEW_CODE_KEY, reviewResultDTO.getProcessInstanceId());
        redisManager.del(redisKey);
    }

    @Override
    public void reviewComplete(Integer reviewResult, OmsOrderReview orderReview, OmsOperator omsOperator) throws OmsBaseException {
        if (Objects.isNull(orderReview)) {
            log.error("未找到审批记录！");
            throw new OmsBaseException("未找到审批记录！");
        }
        if (!Objects.equals(orderReview.getReviewStatus(), OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK)) {
            log.error("审批状态不在待审批！");
            throw new OmsBaseException("审批状态不在待审批！");
        }

        OmsOrderReview updateOrderReview = new OmsOrderReview();
        updateOrderReview.setReviewId(orderReview.getReviewId());
        updateOrderReview.setOrderId(orderReview.getOrderId());
        updateOrderReview.setWorkId(orderReview.getWorkId());
        updateOrderReview.setReviewType(orderReview.getReviewType());
        updateOrderReview.setReviewStatus(Objects.equals(reviewResult, GlobalConsts.YES) ? OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK_SUCCESS : OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK_FAIL);
        Date now = DateUtil.getNow();
        updateOrderReview.setReviewTime(now);
        updateOrderReview.setUpdateTime(now);
        updateOrderReview.setReviewerId(Objects.isNull(omsOperator.getOperatorId()) ? null : omsOperator.getOperatorId().toString());
        updateOrderReview.setReviewer(omsOperator.getOperator());
        orderReviewService.updateById(updateOrderReview);
    }

    /**
     * 生成订单审核模型
     *
     * @param reviewType
     * @param orderId
     * @param workId
     * @param reviewData
     * @param omsOperator 操作人信息
     * @return
     */
    private OmsOrderReview generateOrderReviewModel(Integer reviewType, Long orderId, Long workId, String reviewData, OmsOperator omsOperator) {
        Date now = DateUtil.getNow();
        OmsOrderReview omsOrderReview = new OmsOrderReview();
        omsOrderReview.setCreater(omsOperator.getOperator());
        omsOrderReview.setCreateTime(now);
        omsOrderReview.setOrderId(orderId);
        omsOrderReview.setWorkId(workId);
        omsOrderReview.setReviewType(reviewType);
        omsOrderReview.setReviewStatus(OrderConsts.OMS_ORDER_REVIEW_STATUS_CHECK);
        omsOrderReview.setReviewData(reviewData);
        return omsOrderReview;
    }
}